/********************************************************
 *
 * File Name: PlayerD.java
 *
 * Purpose:
 *
 * Created By: 1337-Player-D
 * Date Created: Wednesday March 16, 2011.
 * Last Modified: Sunday March 27, 2011.
 *
 ********************************************************/
package ca.utoronto.utm.csc301.euchre.player;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;

import ca.utoronto.utm.csc301.euchre.common.Rank;
import ca.utoronto.utm.csc301.euchre.common.Suit;
import ca.utoronto.utm.csc301.euchre.common.Card;
import ca.utoronto.utm.csc301.euchre.gamemanager.GameManager;

public class PlayerD extends AbstractPlayer{
    // Variables and Field(s)
    private List<Card> myCards;
    private Card previous_card  = null;
    private List<Card> played;
    private boolean going_alone = false;
    int i =1;
    private PriorityLevel _level;

    // AbstractPlayer => protected int id;
    // AbstractPlayer => protected GameManager manager;

    /****************************************************
     *               Constructor(s)
     ***************************************************/
    public PlayerD(int id, GameManager gameManager){
        // Pass 'id' and 'gameManager' to AbstractPlayer
        super(id, gameManager);
        played = new ArrayList<Card>();
        // Initialize Variables
        myCards = null;
        _level = null;
    }

    /************************************************
     * This is used for setting the id of a player.
     *
     * @param id It is the id of the player which is
     *      set by the Game Manager.
     ************************************************/
    public void setId(int id){
        // Reset 'this' Players 'id'
        this.id = id;
    }

    /************************************************
     * This method is called when a new hand is started.
     *      It is called by the game manager.
     *
     * @param hand The list of cards for the player.
     ************************************************/
    public void setHand(List<Card> hand){
        // Reset 'this' Players 'hand'
        this.myCards = hand;
        _level = new PriorityLevel(myCards);
       
    }

    /************************************************
     * It called for each player for bidding.
     *
     * @param isTheFirstBid indicates whether
     *          this is the first bidding round or not.
     * @return The trump suit, or null if the player
     *          do not want to make trump.
     ***********************************************/
    public Suit makeTrump(boolean isTheFirstBid) {
        // Determine Bidding Round
        if(isTheFirstBid){
            // Initialize Variables
            Card card = this.manager.getTopCard();

            // Determine if TopCard is our PreferredSuit in our PriorityLevel
            if(_level.getPreferredSuit().isOfTheSameColor(card.getSuit()))
                return (Suit)card.getSuit();
            else
                return (Suit)null;
        }
        else{
            // Determine if have more than 1 cards in our PriorityLevel
            if(_level.topLevel().size() >= 1)
                return (Suit)_level.getPreferredSuit();
            else
                return (Suit)null;
        }
    }


    /***********************************************
     * It shows whether the player wants to go it alone
     *
     * @return true if the player wants to go it alone,
     *          otherwise false.
     ***********************************************/
    
    public void decide_goingAlone(){
        // Initialize Variables
        int position = (this.manager.getDealerId() + this.id) % 4;

        // Determine Position Relative to Dealer
        switch(position){
            // Left to Dealer (first to move)
            case 0:
                // Determine Size of TopLevel PriorityLevel
                if(_level.topLevel().size() == 5 && _level.getValue(_level.topLevel()) > 100)
                	going_alone =  true;
                else
                	going_alone = false;

            // 2nd to Dealer
            case 1:
                // Determine Size of TopLevel PriorityLevel
                if(_level.topLevel().size() == 5 || _level.getValue(_level.topLevel()) > 100)
                	going_alone = true;
                else
                	going_alone = false;

            // 3rd to Dealer
            case 2:
                // Determine Size of TopLevel PriorityLevel
                if(_level.topLevel().size() >= 4 || _level.getValue(_level.topLevel()) > 90)
                	going_alone = true;
                else
                	going_alone= false;

            // Dealer (last to move)
            case 3:
                // Determine Size of TopLevel PriorityLevel
                if(_level.topLevel().size() > 3 || _level.getValue(_level.topLevel()) > 80)
                	going_alone=true;
                else
                	going_alone=false;

            // Default Case (Should never Occur)
        }
        }
    
    
    public boolean isGoingAlone(){
    	return going_alone;
    }

    /***********************************************
     * It is called for the dealer player to pick
     *      the top card up and drop a card from his hand.
     *
     * @return the dropped card.
     **********************************************/
    public Card pick() {
        // Grab Top Card from Kitty
        Card card = manager.getTopCard();

        // Create Temporary Hand
        List<Card> cards = new ArrayList<Card>();

        // Add Our Cards to new Hand
        for(int i=0; i<5; i++)
            cards.add(myCards.get(i));

        // Add Top Card from Kitty
        cards.add(card);

        // Create a new Priority Level
        PriorityLevel level = new PriorityLevel(cards);

        // If the top level of this new PriorityLevel is Greater than our Current PriorityLevel
        //     then we have better cards than before
        if(level.topLevel().size() > _level.topLevel().size()){
            // Temp Variables
            Card removal = null;

            // Remove Lowest Card in our Hand
            for(int i=0; i<5; i++){
                // Determine if Removal Card has been Selected
                if(removal == null)
                       removal = myCards.get(0);
                else
                    // Compare Removal Card Rank Versus Current Iterator Card Rank
                    if(removal.getRank().ordinal() > myCards.get(i).getRank().ordinal())
                        removal = myCards.get(i);
            }

            // Remove Lowest Card
            cards.remove(removal);

            // Create New PriorityLevel with New Hand
            _level = new PriorityLevel(cards);

            // Return GameManager Our Lowest Card
            return removal;
        }
        else
            // Return Kitty to GameManager (Our Hand was Better to Begin with)
            return card;
    }

    /**********************************************
     * It is called when a player must play a card.
     *
     * @return the card played.
     **********************************************/
    public Card play(){
    	
    	//System.out.println("My turn number : " + i);
    	//System.out.println("and I have : " + myCards.size() + " cards");
    	i++;
    	System.out.println(manager.getPlayedCards());
    	//System.out.println("this " + manager.getHistory().size());
    	played.add(previous_card);
    	//System.out.println("hmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" + myCards.size());
        //System.out.println("removing card was : " + myCards.remove(previous_card));
        if(myCards.isEmpty()){
        	return null;
        }
        
        if (myCards.size()==1){
        	//System.out.println("here now");
        	previous_card = myCards.get(0);
        	//played.add(previous_card);
        	//myCards.remove(0);
        	return previous_card;
        }
        	
        if(myCards.size()==5){
        	// first turn
        	decide_goingAlone();
        }
        Card lead_card = null;
        Suit lead_suit = null;
        //System.out.println("Dealer id: " + manager.getDealerId());
        Set<Integer> played = manager.getPlayedCards().keySet();
        if (played != null){
        	for (int i:played){
        		lead_card = manager.getPlayedCards().get(i);
        		break;
        	}
        //System.out.println("played: " + played);
        }
        
       
        if (lead_card !=null){
        //System.out.println("Lead card: " + lead_card.getSuit());
        //System.out.println("Lead card: " + lead_card.getRank());
        lead_suit = lead_card.getSuit();
        }
        System.out.println("Lead suit is: " + lead_suit);
        Suit trump_suit = this.manager.getTrumpSuit();
        // Return Last Card in my Hand Always
        System.out.println("play called");
        
        if (getRating(lead_card)==23){
        	lead_suit = lead_card.getSuit().getSuitOfTheSameColor();
        	System.out.print("------------------Lead suit changed-------------");
        }
       // if(myCards.size()==1){
       // 	previous_card = max_rating
       // }
        
        
        if (manager.getPlayedCards().size() ==0){
        	// Play highest
        	
        	 previous_card = max_rating(myCards);
             return previous_card;
        }
        else if (manager.getPlayedCards().size() >= 1){
            // Follow the card lead if you have of that Suit, else play whatever you want
            if (has_card_of_suit(lead_suit)){
            	Card max_card_of_suit = max_rating_with_suit(myCards,lead_suit);
            	if (getRating(max_card_of_suit) > max_current_played()){
            		if (getRating(max_card_of_suit)==23){
            			//max_card_of_suit = second_max_rating_with_suit(myCards,lead_suit);
            		}
            		previous_card = max_card_of_suit;
            		return previous_card;	
            	}
            	else{
            		Card min_card_of_suit = min_rating_with_suit(myCards,lead_suit);
            		previous_card = min_card_of_suit;
            		return previous_card;
            		
            	}
            }
            else{
            	System.out.println("Not required to follow the suit here");
            	// but check if you have the left bower if yes play it.
            	Card left_bower = have_left_bower(lead_card,manager.getTrumpSuit());
            	if (left_bower !=null){
            		previous_card = left_bower;
            		return previous_card;
            	}
            	Card max_card = max_rating(myCards);
            	System.out.println("Max card: " + max_card);
            	if (getRating(max_card) > max_current_played()){
            		previous_card = max_card;
            		return previous_card;	
            	}
            	else{
            		Card min_card = min_rating(myCards);
            		previous_card = min_card;
            		return previous_card;	
            	}
                // pick a random card
            }
           
        }
       
        else{
        	System.out.println(manager.getPlayedCards().size());
            System.out.println("should never go here");
            return null;
        }

        
       
    }
    
    private Card have_left_bower(Card lead_card, Suit suit) {
    	for (Card c: myCards){
    		if (c.getSuit().getSuitOfTheSameColor().ordinal() == suit.ordinal()){
    			System.out.println("Let me know");
    			System.out.println(c.getRank());
    			System.out.println(getRating(c));
    			if (getRating(c) == 23){
    				return c;
    			}
    		}
    		
    	}
		return null;
	}

	private int max_current_played() {
    	Card max_card = null;
    	int max_rank = 0;
    	for (int i: manager.getPlayedCards().keySet()){
    		if (getRating(manager.getPlayedCards().get(i)) > max_rank){
    			max_rank = getRating(manager.getPlayedCards().get(i));
    			max_card = manager.getPlayedCards().get(i);
    		}
    	}
		// TODO Auto-generated method stub
		return max_rank;
	}

	

    public Suit maxSuit(){
        
        int[] frequency= new int[5]; 
            int count1=0;
            int count2=0;
            int count3=0;
            int count4=0;
            int count5=0;
            Suit maxSuit = null;
            
        Suit test_suit1 = this.myCards.get(0).getSuit();
        Suit test_suit2 = this.myCards.get(1).getSuit();
        Suit test_suit3 = this.myCards.get(2).getSuit();
        Suit test_suit4 = this.myCards.get(3).getSuit();
        Suit test_suit5 = this.myCards.get(4).getSuit();
        
        for(int i=0; i<5 ; i++){
        if (test_suit1.ordinal() == this.myCards.get(i).getSuit().ordinal()){
            count1=count1+1;                        
           }
            }
        
        for(int i=0; i<5 ; i++){
            if (test_suit2.ordinal() == this.myCards.get(i).getSuit().ordinal()){
                count2=count2+1;                        
               }
            }
                
        for(int i=0; i<5 ; i++){
            if (test_suit3.ordinal() == this.myCards.get(i).getSuit().ordinal()){
                count3=count3+1;                        
               }
            }
        
        for(int i=0; i<5 ; i++){
            if (test_suit4.ordinal() == this.myCards.get(i).getSuit().ordinal()){
                count4=count4+1;                        
               }
            }
        
        for(int i=0; i<5 ; i++){
            if (test_suit5.ordinal() == this.myCards.get(i).getSuit().ordinal()){
                count5=count5+1;                        
               }
            }
        
        frequency[0] = count1;
        frequency[1] = count2;
        frequency[2] = count3;
        frequency[3] = count4;
        frequency[4] = count5;
        
        Arrays.sort(frequency);
        
        if(frequency[4]>=3){
            System.out.println("hello");
            if (frequency[4] == count1){maxSuit=test_suit1;return maxSuit;}
            if (frequency[4] == count2){maxSuit=test_suit2;return maxSuit;}
            if (frequency[4] == count3){maxSuit=test_suit3;return maxSuit;}
            if (frequency[4] == count4){maxSuit=test_suit4;return maxSuit;}
            if (frequency[4] == count5){maxSuit=test_suit5;return maxSuit;}
        }
        
        //else {return (Suit)null;}     
        
        return (Suit)null;
}
    
    private boolean has_card_of_suit(Suit c){
    	if (c == null){
    		return false;
    	}
        for (Card g: this.myCards){
        	System.out.println("hey");
            if(g.getSuit().ordinal() == c.ordinal()){
                return true;
            }
        }
        return false;
    }
    private int getRating (Card card) {
        /*
        24 - jack of trump
        23 - jack of same color as trump
        22 - ace of trump
        21 - king of trump
        20 - queen of trump
        19 - 10 of trump
        18 - 9 of trump
        17 - ace of lead suit
        16 - king of lead suit
        15 - queen of lead suit
        14 - jack of lead suit
        13 - 10 of lead suit
        12 - 9 of lead suit
        11 - all the rest
         */
    	if (card==null){
    		System.out.println("here");
    		return 11;
    	}
        Suit trump = this.manager.getTrumpSuit();
        //System.out.println("Dealer id: " +this.manager.getDealerId());
        int firstPlayerId = (this.manager.getDealerId() + 1) % 4;
        Card leadCard = this.manager.getPlayedCards().get(0);
        Suit leadSuit = null;
        if (leadCard != null){
        	leadSuit = this.manager.getPlayedCards().get(0).getSuit();
        }

        //Suit leadSuit = this.manager.getPlayedCards().get(0)
        int rating = 11;
        Suit suit = card.getSuit();
        Rank rank = card.getRank();
        //System.out.println(rank == Rank.JACK);
     
        //System.out.println(suit.getSuitOfTheSameColor().ordinal() == trump.ordinal());
        //System.out.println(trump);
        if (rank == Rank.JACK && suit == trump)
            rating = 24;
        
        else if (rank == Rank.JACK && suit.getSuitOfTheSameColor().ordinal() == trump.ordinal())
            return 23;
        else if (rank == Rank.ACE && suit == trump)
            rating = 22;
        else if (rank == Rank.KING && suit == trump)
            rating = 21;
        else if (rank == Rank.QUEEN && suit == trump)
            rating = 20;
        else if (rank == Rank.TEN && suit == trump)
            rating = 19;
        else if (rank == Rank.NINE && suit == trump)
            rating = 18;
        else if (suit == leadSuit) {
            if (rank == Rank.ACE)
                rating = 17;
            else if (rank == Rank.KING)
                rating = 16;
            else if (rank == Rank.QUEEN)
                rating = 15;
            else if (rank == Rank.JACK)
                rating = 14;
            else if (rank == Rank.TEN)
                rating = 13;
            else if (rank == Rank.NINE)
                rating = 12;
        }
            
        return rating;
    }
    
    public Card max_rating(List<Card> myCards2){
    	Card max = null;
    	int rank = 0;
    	for (Card c: myCards2){
    		if (getRating(c) > rank){
    			max = c;
    			rank = getRating(c);
    		}
    	}
		return max;
    	
    }
    
    public Card min_rating(List<Card> myCards2){
    	Card min = null;
    	int rank = 100;
    	for (Card c: myCards2){
    		if (getRating(c) < rank){
    			min = c;
    			rank = getRating(c);
    		}
    	}
		return min;
    	
    }
    
    public Card max_rating_with_suit(List<Card> cards,Suit s){
    	Card max = null;
    	int rank = 0;
    	for (Card c: cards){
    		if (c.getSuit().ordinal() == s.ordinal()){
    			if (getRating(c) > rank && getRating(c) != 23){
    				max = c;
    				rank = getRating(c);
    			}
    		}
    	}
		return max;
    	
    }
    
    public Card second_max_rating_with_suit(List<Card> cards,Suit s){
    	Card max = null;
    	int rank = 0;
    	for (Card c: cards){
    		if (c.getSuit().ordinal() == s.ordinal()){
    			if (getRating(c) > rank){
    				max = c;
    				rank = getRating(c);
    			}
    		}
    	}
		return max;
    	
    }
    
    public Card min_rating_with_suit(List<Card> cards,Suit s){
    	Card min = null;
    	int rank = 100;
    	for (Card c: cards){
    		if (c.getSuit().ordinal() == s.ordinal()){
    			if (getRating(c) < rank){
    				min = c;
    				rank = getRating(c);
    			}
    		}
    	}
		return min;
    	
    }
    
}
